package Core

import spinal.core._

import scala.collection.mutable.ArrayBuffer

object CPUSimpleConfig {
    def apply(plugins : Seq[Plugin[CPUSimple]] = ArrayBuffer()) : CPUSimpleConfig = {
        val config = CPUSimpleConfig()
        config.plugins ++= plugins
        config
    }
}

case class CPUSimpleConfig() {
    var addressWidth = 32
    var XLEN = 32
    var LEN = 32
    var PredictorHistoryLen = 7
    val plugins = ArrayBuffer[Plugin[CPUSimple]]()
    def add(that : Plugin[CPUSimple]) : this.type = {plugins += that;this}
    def find[T](clazz: Class[T]): Option[T] = {
        plugins.find(_.getClass == clazz) match {
            case Some(x) => Some(x.asInstanceOf[T])
            case None => None
        }
    }
    def get[T](clazz: Class[T]): T = {
        plugins.find(_.getClass == clazz) match {
            case Some(x) => x.asInstanceOf[T]
        }
    }

    object AluCtrlEnum extends SpinalEnum(binarySequential){
        val ADD, SUB, SLT, SLTU, NOR, XOR, SLL, SRL, SRA, AND, OR, LUI, JIRL, BEQ, BNE, BLT, BGE, BLTU, BGEU, CSR,
        MUL, MULH, MULHU, DIV, DIVU, BL = newElement()
    }

    object MemCtrlEnum extends SpinalEnum(binarySequential){
        val IDLE, LD_B, LD_H, LD_W, LD_BU, LD_HU, ST_B, ST_H, ST_W, LL, SC = newElement()
    }

    object CsrCtrlEnum extends SpinalEnum(binarySequential) {
        val CSRRD, CSRWR, CSRXCHG, SYSCALL, ERTN, BREAK, ADEF, ALE, INE = newElement()
    }

    object PC extends Stageable(UInt(addressWidth bits))
    object PC_NEXT extends Stageable(UInt(addressWidth bits))
    object INSTRUCTION extends Stageable(Bits(32 bits))
    object PREDICT_VALID extends Stageable(Bool())
    object PREDICT_TAKEN extends Stageable(Bool())
    object PREDICT_PC extends Stageable(UInt(addressWidth bits))
    object FETCH_DEC_JIRL extends Stageable(Bool())
    object FETCH_DEC_BRANCH extends Stageable(Bool())
    object FETCH_DEC_IMM extends Stageable(Bits(XLEN bits))

    object RS1 extends Stageable(Bits(XLEN bits))
    object RS2 extends Stageable(Bits(XLEN bits))
    object RS1_ADDR extends Stageable(UInt(5 bits))
    object RS2_ADDR extends Stageable(UInt(5 bits))
    object RS1_REQ extends Stageable(Bool())
    object RS2_REQ extends Stageable(Bool())
    object RS1_FROM_EX extends Stageable(Bool())
    object RS2_FROM_EX extends Stageable(Bool())
    object RS1_FROM_MEM extends Stageable(Bool())
    object RS2_FROM_MEM extends Stageable(Bool())
    object RD  extends Stageable(Bits(XLEN bits))
    object RD_ADDR extends Stageable(UInt(5 bits))
    object RD_WEN extends Stageable(Bool())
    object LINK_ADDR extends Stageable(Bits(XLEN bits))
    object IS_LOAD extends Stageable(Bool())
    object IS_STORE extends Stageable(Bool())
    object MEM_CTRL extends Stageable(Bits(MemCtrlEnum.ST_W.asBits.getWidth bits))
    object ALU_CTRL extends Stageable(Bits(AluCtrlEnum.ADD.asBits.getWidth bits))
    object LSU_RDATA extends Stageable(Bits(XLEN bits))
    object LSU_WDATA extends Stageable(Bits(XLEN bits))
    object LSU_HOLD extends Stageable(Bool())
    //object ALU_WORD extends Stageable(Bool())


    object BPU_PC_NEXT extends Stageable(UInt(addressWidth bits))
    object BPU_BRANCH_TAKEN extends Stageable(Bool())

    // exe stage insert
    object ALU_RESULT extends Stageable(Bits(XLEN bits))
    object MEM_WDATA extends Stageable(Bits(XLEN bits))
    object BRANCH_TAKEN extends Stageable(Bool())
    //object BRANCH_OR_JALR extends Stageable(Bool())
    object BRANCH_OR_JUMP extends Stageable(Bool())
    object BRANCH_HISTORY extends Stageable(UInt(PredictorHistoryLen bits))
    object IS_CALL extends Stageable(Bool())
    object IS_RETURN extends Stageable(Bool())
    object IS_JUMP extends Stageable(Bool())
    object REDIRECT_VALID extends Stageable(Bool())
    object REDIRECT_PC_NEXT extends Stageable(UInt(addressWidth bits))
    object LLBIT extends Stageable(Bool())
    object LLBIT_DATA extends Stageable(Bool())
    object LLBIT_WE extends Stageable(Bool())

    object CSR_CTRL extends Stageable(Bits(CsrCtrlEnum.CSRRD.asBits.getWidth bits))
    object CSR_ADDR extends Stageable(Bits(14 bits))
    object CSR_WEN extends Stageable(Bool())
    object CSR_RDATA extends Stageable(Bits(XLEN bits))
    object IS_CSR extends Stageable(Bool())
    object CSR_PC extends Stageable(Bits(XLEN bits))
    object INT_PC extends Stageable(Bool())
    object CSR_CODE extends Stageable(Bits(15 bits))
    object IS_ALE extends Stageable(Bool())
}

class CPUSimple(val config: CPUSimpleConfig) extends Component with Pipeline {
    type T = CPUSimple

    def newStage() : Stage = { val s = new Stage; stages += s; s}
    val fetch     = newStage()
    val decode    = newStage()
    val execute   = newStage()
    val memaccess = newStage()
    val writeback = newStage()
    //val writebackStage = writeback
    plugins ++= config.plugins
}


